<?php

namespace App\Services;

use App\Constants\CacheKey;
use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use App\Model\User;
use App\Services\Dao\UserDao;
use Hyperf\Redis\Redis;
use Hyperf\Utils\Codec\Json;
use Hyperf\Utils\Traits\StaticInstance;

class UserAuth
{
    use StaticInstance;

    const X_TOKEN = 'x-token';

    /**
     * @var int
     */
    protected $userId = 0;

    /**
     * @var string|null
     */
    protected $token;

    /**
     * @var User
     */
    protected $user;

    /**
     * 初始化用户数据
     * @param User $user
     * @param string|null $token
     * @return $this
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function init(User $user, ?string $token = null): UserAuth
    {
        $this->user = $user;
        $this->userId = $user->id;
        $this->token = $token ?? md5(uniqid() . time());

        di()->get(Redis::class)->set($this->getCacheKey(), Json::encode([
            "userId" => $this->userId,
            "nickname" => $user->nickname,
        ]), CacheKey::USER_AUTH_TTL);
        return $this;
    }

    /**
     * @param $token
     * @return $this
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function reload($token): UserAuth
    {
        $this->token = $token;
        $cache = di()->get(Redis::class)->get($this->getCacheKey());
        if ($cache && $data = Json::decode($cache)) {
            $this->userId = intval($data['userId'] ?? 0);
        }
        return $this;
    }

    /**
     * 校验token
     * @return $this
     */
    public function authCheck(): UserAuth
    {
        if ($this->getUserId() === 0) {
            throw new BusinessException(ErrorCode::TOKEN_INVALID, ErrorCode::getMessage(ErrorCode::TOKEN_INVALID));
        }
        return $this;
    }

    public function getUserId(): int
    {
        return $this->userId;
    }

    public function getToken(): ?string
    {
        return $this->token;
    }

    public function getUser(): User
    {
        if ($this->user) {
            return $this->user;
        }
        $userId = $this->authCheck()->getUserId();

        $this->user = di()->get(UserDao::class)->first($userId, true);
        return $this->user;
    }

    protected function getCacheKey(): string
    {
        if (empty($this->token)) {
            throw new BusinessException(ErrorCode::INVALID_TOKEN_INIT, ErrorCode::getMessage(ErrorCode::INVALID_TOKEN_INIT));
        }

        return CacheKey::USER_AUTH_KEY . $this->token;
    }
}
